use movie::error::{Error, Result};
use regex::Regex;
use serde::{Deserialize, Serialize};
use std::{collections::HashMap, fs::File, io::{BufRead, BufReader, BufWriter, Write}, path::{Path, PathBuf}};

#[derive(Debug, Default, Serialize, Deserialize, Clone)]
pub struct Movie {
    pub disc: u32,
    pub year: String,
    pub title: String,
    pub remark: Option<String>,
    pub no: u32,
}

pub struct MoviePath {
    path: PathBuf,
}

impl MoviePath {
    pub fn new(path: PathBuf) -> Self {
        println!("{:?}", path);
        if let Some(parent_dir) = path.parent() {
            println!("父目录: {:?}", parent_dir);
        } else {
            println!("无法获取父目录（可能是根路径）");
        }
        Self {
            path: path.parent().unwrap().to_path_buf(),
        }
    }

    pub fn read_txt_to_json(&self, file: File) {
        let reader = BufReader::new(file);
        let mut disc_no = 0u32;
        let disc_regex = Regex::new(r"^(\d+)\.$").unwrap();
        let movie_regex = Regex::new(r"^(\d{4})(.*?)(（儿童）)?$").unwrap();
        let mut movies = Vec::new();
        for line in reader.lines().map(|line| line.unwrap().trim().to_string()).filter(|line| !line.is_empty()) {
            if let Some(no) = self.disc_number(line.clone(), &disc_regex) {
                disc_no = no as u32;
            } else {
                if let Some(movie) = self.parse_movie(disc_no, line, &movie_regex) {
                    movies.push(movie);
                }
            }
        }
        self.save_to_json(movies);
    }

    fn save_to_json(&self, mut movies: Vec<Movie>) {
        let movie_list = self.add_no_to_movies(&mut movies);
        let data = serde_json::to_string_pretty(&movie_list).unwrap();
        let path = self.path.join("DVDs.json");
        println!("{:?}", path);
        let mut buf_writer = BufWriter::with_capacity(data.len(), File::create(path).unwrap());
        buf_writer.write_all(data.as_bytes()).unwrap();
        println!("数据已写入文件");
    }

    /// 1. mut movies: &mut Vec<Movie>中 &mut Vec<Movie>说明传入的是一个可变的Vec<Movie>, mut movies说明movies里的属性可变
    /// 2. 'a 是告诉编译器：返回的引用与输入的 movies 引用指向同一个 Vec<Movie> 数据，且它们的存活时间必须一致。
    fn add_no_to_movies<'a>(&self, movies: &'a mut Vec<Movie>) -> &'a mut Vec<Movie> {
        let mut disc_counter: HashMap<u32, u32> = HashMap::new();
        for movie in &mut *movies {
            let count = disc_counter.entry(movie.disc).or_insert(0);
            *count += 1;
            movie.no = *count;
        }
        movies
    }

    fn parse_movie(&self, disc_no: u32, line: String, re: &Regex) -> Option<Movie> {
        re.captures(&line).map(|m| Movie {
            disc: disc_no,
            year: m.get(1).unwrap().as_str().trim().to_string(),
            title: m.get(2).unwrap().as_str().trim().to_string(),
            remark: m.get(3).map(|c| c.as_str().trim().to_string()),
            no: 0,
        })
    }

    fn disc_number(&self, line: String, re: &Regex) -> Option<u32> {
        if let Some(cap) = re.captures(&line) {
            cap.get(1).map(|m| m.as_str().parse::<u32>().unwrap())
        } else {
            None
        }
    }
}

/// 查询 修改和删除的工具
#[derive(Clone, Debug, Default)]
pub struct SearchMovie {
    pub disc: u32,
    pub disc_op: String,
    pub year: String,
    pub year_op: String,
    pub title: String,
    pub title_op: String,
    pub remark: String,
    pub remark_op: String,
    pub no: u32,
    pub no_op: String,
    pub page: u32,
    pub size: u32,
}

fn matches_condition(movie: &Movie, search: &SearchMovie) -> bool {
    if !match_operation(search.disc_op.as_str(), movie.disc, search.disc) {
        return false;
    }

    if !match_str_operation(search.year_op.as_str(), &movie.year, &search.year) {
        return false;
    }

    if !match_str_operation(search.title_op.as_str(), &movie.title, &search.title) {
        return false;
    }

    if !match_optional_str_operation(search.remark_op.as_str(), &movie.remark, &search.remark) {
        return false;
    }

    if !match_operation(search.no_op.as_str(), movie.no, search.no) {
        return false;
    }

    true
}

fn match_operation<T: PartialEq + PartialOrd>(op: &str, value: T, target: T) -> bool {
    match op {
        "==" => value == target,
        "!=" => value != target,
        "<" => value < target,
        ">" => value > target,
        "<=" => value <= target,
        ">=" => value >= target,
        _ => true, // 未知操作符默认匹配
    }
}

fn match_str_operation(op: &str, value: &str, target: &str) -> bool {
    if target.is_empty() {
        return true;
    }
    match op {
        "==" => value == target,
        "!=" => value != target,
        "contains" => value.contains(target),
        "starts_with" => value.starts_with(target),
        "ends_with" => value.ends_with(target),
        _ => true, // 未知操作符默认匹配
    }
}

/// 处理Option<String>类型的匹配函数
fn match_optional_str_operation(op: &str, value: &Option<String>, target: &str) -> bool {
    // 如果查询条件为空，直接匹配
    if target.is_empty() {
        return true;
    }
    // 匹配逻辑：根据Option的值和操作符处理
    match (value, op) {
        // 当电影的remark为None时
        (None, "is_none") => true,          // 匹配"is_none"操作
        (None, "is_some") => false,         // 不匹配"is_some"操作
        (None, "==") => target.is_empty(),  // 只有目标为空时才匹配
        (None, "!=") => !target.is_empty(), // 目标非空时匹配
        (None, _) => false,                 // 其他操作都不匹配
        // 当电影的remark为Some时（安全解包）
        (Some(val), _) => match_str_operation(op, val, target),
    }
}

pub fn disc_list(path: &Path, search: SearchMovie) -> Result<Vec<Movie>> {
    let file = File::open(path)?;
    let json_data: Vec<Movie> = serde_json::from_reader(file).unwrap();
    let filtered_movies: Vec<Movie> = json_data.into_iter().filter(|movie| matches_condition(&movie.clone(), &search.clone())).collect();
    if search.page == 0 && search.size == 0 {
        return Ok(filtered_movies);
    }
    if search.page == 0 {
        return Err(Error::Internal("页码必须大于0".to_string()));
    }
    if search.size == 0 {
        return Err(Error::Internal("每页大小必须大于0".to_string()));
    }
    let total = filtered_movies.len() as u32;
    let start = (search.page - 1) * search.size;
    if start >= total {
        return Ok(Vec::new());
    }
    let end = std::cmp::min(start + search.size, total) as usize;
    let start = start as usize;
    // 截取分页数据
    let paginated_items = filtered_movies[start..end].to_vec();
    Ok(paginated_items)
}

pub fn disc_add(path: &Path, movies: Vec<Movie>) -> Result<usize> {
    let mut result = disc_list(path, SearchMovie::default())?;
    let mut added_count = 0;
    for mut movie in movies {
        let target_disc = movie.disc;
        let max_no = result.iter().filter(|m| m.disc == target_disc).map(|m| m.no).max().unwrap_or(0);
        movie.no = max_no + 1;
        result.push(movie);
        added_count += 1;
    }
    result.sort_by(|a, b| a.disc.cmp(&b.disc).then_with(|| a.no.cmp(&b.no)));
    serde_json::to_writer(File::create(path)?, &result)?;
    Ok(added_count)
}

pub fn disc_edit<F>(path: &Path, search: SearchMovie, modify_fn: F) -> Result<usize> where F: Fn(&mut Movie){
    let mut result = disc_list(path, SearchMovie::default())?;
    let mut modified_count = 0;
    for movie in &mut result {
        if matches_condition(movie, &search) {
            modify_fn(movie);
            modified_count += 1;
        }
    }
    if modified_count > 0 {
        let mut disc_counter: HashMap<u32, u32> = HashMap::new();
        for movie in &mut result {
            let count = disc_counter.entry(movie.disc).or_insert(0);
            *count += 1;
            movie.no = *count;
        }
        serde_json::to_writer(File::create(path)?, &result)?
    }
    Ok(modified_count)
}

pub fn disc_delete(path: &Path, search: SearchMovie) -> Result<usize> {
    let mut result = disc_list(path, SearchMovie::default())?;
    let original_count = result.len();
    // 保留不匹配条件的电影（即删除匹配条件的）
    result.retain(|m| !matches_condition(m, &search));
    let deleted_count = original_count - result.len();
    if deleted_count > 0 {
        let mut disc_counter: HashMap<u32, u32> = HashMap::new();
        for movie in &mut result {
            let count = disc_counter.entry(movie.disc).or_insert(0);
            *count += 1;
            movie.no = *count;
        }
        serde_json::to_writer(File::create(path)?, &result)?
    }
    Ok(deleted_count)
}


#[cfg(test)]
mod tests {

    use super::*;

    #[test]
    fn test_disc_list() {
        let path = Path::new("/root/rust_workspace/movie/DVDs.json");
        let search1 = SearchMovie {
            disc: 1,
            disc_op: "==".to_string(),
            year: "".to_string(),
            year_op: "".to_string(),
            title: "".to_string(),
            title_op: "".to_string(),
            remark: "".to_string(), // 空字符串表示不过滤
            remark_op: "is_none".to_string(),
            no: 0,
            no_op: "".to_string(),
            page: 1,
            size: 20,
        };
        let result1 = disc_list(path, search1).unwrap();
        for movie in result1.iter() {
            println!("{:?}", movie);
        }
        println!("===================================================");
        let search2 = SearchMovie {
            disc: 1,
            disc_op: "==".to_string(),
            year: "".to_string(),
            year_op: "".to_string(),
            title: "".to_string(),
            title_op: "".to_string(),
            remark: "".to_string(), // 空字符串表示不过滤
            remark_op: "is_none".to_string(),
            no: 10,
            no_op: "<".to_string(),
            page: 1,
            size: 20,
        };
        let result2 = disc_list(path, search2).unwrap();
        for movie in result2.iter() {
            println!("{:?}", movie);
        }
        println!("===================================================");
        let search3 = SearchMovie {
            disc: 0,
            disc_op: "".to_string(),
            year: "".to_string(),
            year_op: "".to_string(),
            title: "".to_string(),
            title_op: "".to_string(),
            remark: "（儿童）".to_string(), // 空字符串表示不过滤
            remark_op: "contains".to_string(),
            no: 0,
            no_op: "".to_string(),
            page: 1,
            size: 20,
        };
        let result3 = disc_list(path, search3).unwrap();
        for movie in result3.iter() {
            println!("{:?}", movie);
        }
        println!("===================================================");
        let search4 = SearchMovie {
            disc: 10,
            disc_op: "==".to_string(),
            year: "".to_string(),
            year_op: "".to_string(),
            title: "".to_string(),
            title_op: "".to_string(),
            remark: "".to_string(), // 空字符串表示不过滤
            remark_op: "".to_string(),
            no: 0,
            no_op: "".to_string(),
            page: 1,
            size: 20,
        };
        let result4 = disc_list(path, search4).unwrap();
        for movie in result4.iter() {
            println!("{:?}", movie);
        }
        println!("===================================================");
        let search5 = SearchMovie {
            disc: 0,
            disc_op: "".to_string(),
            year: "".to_string(),
            year_op: "".to_string(),
            title: "".to_string(),
            title_op: "".to_string(),
            remark: "".to_string(), // 空字符串表示不过滤
            remark_op: "".to_string(),
            no: 0,
            no_op: "".to_string(),
            page: 0,
            size: 0,
        };
        let result5 = disc_list(path, search5).unwrap();
        for movie in result5.iter() {
            println!("{:?}", movie);
        }

        println!("===================================================");
        let search6 = SearchMovie {
            disc: 1,
            disc_op: "".to_string(),
            year: "".to_string(),
            year_op: "".to_string(),
            title: "".to_string(),
            title_op: "".to_string(),
            remark: "".to_string(), // 空字符串表示不过滤
            remark_op: "".to_string(),
            no: 1,
            no_op: "==".to_string(),
            page: 1,
            size: 1,
        };
        let result6 = disc_list(path, search6).unwrap();
        for movie in result6.iter() {
            println!("{:?}", movie);
        }
    }

    #[test]
    fn test_add(){
        let path = Path::new("/root/rust_workspace/movie/DVDs.json");
        let movies =  vec![
            Movie {
                disc: 1,
                year: "2005".to_string(),
                title: "蝙蝠侠：侠影之谜 Batman Begins 2005.mkv".to_string(),
                remark: Some("诺兰蝙蝠侠三部曲首部曲".to_string()),
                no: 0,
            },
            Movie {
                disc: 1,
                year: "2008".to_string(),
                title: "蝙蝠侠：黑暗骑士 The Dark Knight 2008.mkv".to_string(),
                remark: Some("IMDB评分9.0".to_string()),
                no: 0,
            },
            Movie {
                disc: 2,
                year: "2012".to_string(),
                title: "复仇者联盟 The Avengers 2012.mkv".to_string(),
                remark: None,
                no: 0,
            },
            Movie {
                disc: 3,
                year: "1999".to_string(),
                title: "黑客帝国 The Matrix 1999.mkv".to_string(),
                remark: Some("经典科幻电影".to_string()),
                no: 0,
            },
            Movie {
                disc: 3,
                year: "2003".to_string(),
                title: "黑客帝国2：重装上阵 The Matrix Reloaded 2003.mkv".to_string(),
                remark: None,
                no: 0,
            },
            Movie {
                disc: 4,
                year: "2014".to_string(),
                title: "星际穿越 Interstellar 2014.mkv".to_string(),
                remark: Some("诺兰作品，硬科幻".to_string()),
                no: 0,
            },
        ];
        let added_count = disc_add(path, movies).unwrap();
        println!("添加了 {} 条记录", added_count);
        let search = SearchMovie {
            disc: 5,
            disc_op: "<".to_string(),
            page: 1,
            size: 200,
            ..Default::default()
        };
        let result = disc_list(path, search).unwrap();
        for movie in result.iter() {
            println!("{:?}", movie);
        }
    }

    #[test]
    fn test_edit(){
        let path = Path::new("/root/rust_workspace/movie/DVDs.json");
        // 修改所有disc=1且年份为2006的电影标题
        let search = SearchMovie {
            disc: 1,
            disc_op: "==".to_string(),
            year: "2006".to_string(),
            year_op: "==".to_string(),
            ..Default::default()
        };
        let modified = disc_edit(path, search, |movie| {
            movie.title = format!("[经典] {}", movie.title);
            movie.remark = Some("已标记为经典影片".to_string());
        }).unwrap();
        println!("修改了 {} 条记录", modified);
        let search = SearchMovie {
            disc: 1,
            disc_op: "==".to_string(),
            ..Default::default()
        };
        let result = disc_list(path, search).unwrap();
        for movie in result.iter() {
            println!("{:?}", movie);
        }
    }

    #[test]
    fn test_delete(){
        let path = Path::new("/root/rust_workspace/movie/DVDs.json");
        let search = SearchMovie {
            remark: "已标记为经典影片".to_string(),
            remark_op: "==".to_string(),
            ..Default::default()
        };
        let deleted = disc_delete(path, search).unwrap();
        println!("删除了 {} 条记录", deleted);
        let search = SearchMovie {
            disc: 1,
            disc_op: "==".to_string(),
            ..Default::default()
        };
        let result = disc_list(path, search).unwrap();
        for movie in result.iter() {
            println!("{:?}", movie);
        }
    }

    #[test]
    fn test_default(){
       println!("{:?}", SearchMovie::default());
        let search = SearchMovie {
            page: 10,
            size: 10,
            ..Default::default() // 其他搜索字段用默认值（不影响查询全部）
        };
       println!("{:?}", search);
    }
}
